﻿using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace per.esmool.utils {
    public class WorkDateKit {

        private readonly Dictionary<DateTime, ExceptionalDate> exceptionalDates;

        public bool SaturdayIsWorkDate { get; set; } = false;

        public bool SundayIsWorkDate { get; set; } = false;

        public int StartTimeOfDay { get; set; } = 8 * 60;

        public int EndTimeOfDay { get; set; } = 18 * 60;

        public int BreakStartTimeOfDay { get; set; } = 12 * 60;

        public int BreakEndTimeOfDay { get; set; } = 14 * 60;

        public int TotalDurtyTimeOfDay => this.EndTimeOfDay - this.StartTimeOfDay;

        public int BreakTimeOfDay => this.BreakEndTimeOfDay - this.BreakStartTimeOfDay;

        public int DurtyTimeOfDay => this.TotalDurtyTimeOfDay - BreakEndTimeOfDay;

        public WorkDateKit(string exceptionalDatesFilepath) {
            var qs = YamlKit.ParseFile<JArray>(exceptionalDatesFilepath);
            this.exceptionalDates = (from item in qs
                                    let json = item as JObject
                                    select new ExceptionalDate {
                                        Date = json.Value<DateTime>("date"),
                                        WorkDate = json.Value<bool>("workdate")
                                    }).ToDictionary(x => x.Date);
        }

        public DateTime NextDay(DateTime date) {
            var today = date;
            while (true) {
                var next = today.Add(TimeSpan.FromDays(1));

                if (!this.exceptionalDates.ContainsKey(next)) {
                    var exception = this.exceptionalDates[next];
                    if (exception.WorkDate)
                        return next;
                    continue;
                }

                var isSaturDay = next.Day == (int)DayOfWeek.Saturday;
                var isSunday = next.Day == (int)DayOfWeek.Sunday;
                if (isSaturDay && this.SaturdayIsWorkDate)
                    return next;
                if (isSunday && this.SundayIsWorkDate)
                    return next;
            }
        }

        public DateTime AddWorkMinutes(DateTime start, int minutes) {
            (var date, var time) = this.AlignToStartOfDay(start, minutes);
            while (time > this.DurtyTimeOfDay) {
                minutes -= this.DurtyTimeOfDay;
                date = this.NextDay(date);
            }

            int toAdd = this.StartTimeOfDay + minutes;
            if (toAdd > this.BreakStartTimeOfDay)
                toAdd += this.BreakTimeOfDay;
            var result = date + TimeSpan.FromMinutes(toAdd);
            return result;
        }

        public (DateTime, int) AlignToStartOfDay(DateTime startTime, int workMinuteOffset) {
            var date = startTime.Date + TimeSpan.FromMinutes(this.StartTimeOfDay);
            var time = startTime.TimeOfDay.Minutes;
            
            if (time > this.StartTimeOfDay && time < this.BreakStartTimeOfDay)
                workMinuteOffset += startTime.TimeOfDay.Minutes - this.StartTimeOfDay;
            else if (time > this.BreakStartTimeOfDay && time < this.BreakEndTimeOfDay)
                workMinuteOffset += this.BreakStartTimeOfDay - this.StartTimeOfDay;
            else if (time > this.BreakEndTimeOfDay && time < this.EndTimeOfDay)
                workMinuteOffset += this.BreakStartTimeOfDay - this.StartTimeOfDay + time - this.BreakEndTimeOfDay;
            else
                workMinuteOffset += this.DurtyTimeOfDay;

            return (date, workMinuteOffset);
        }
    }

    class ExceptionalDate {
        public DateTime Date { get; set; }
        public bool WorkDate { get; set; }
    }
}
